#!/usr/bin/env python
# -*- coding: utf-8 -*-
#

import  os, os.path

import  md5
import  tempfile
import  time
import  random

import  zxstore.store as store

from    zxstore import KeyDupError,  KeyNotFoundError


class    TestStore(object) :

    key_range = 100000

    def    map(self, key) :
        m = md5.new()
        m.update(key)
        digest = m.hexdigest()
        x = digest[:1]
        return int(x, 16)

    def    setUp(self) :
        tmpdir = tempfile.gettempdir()
        if tmpdir is None :
            raise RuntimeError, 'invalid tmpdir'
        self.store_path = tmpdir + os.sep + 'storem' + os.sep + 'store'
        if os.path.isdir(self.store_path) :
            import    glob
            files = glob.glob(self.store_path + os.sep + '*' + os.sep + '*.*')
            for file in files :
                os.remove(file)
        else :
            os.makedirs(self.store_path, 0777)

    def    tearDown(self) :
        self.storer = None

    def    testInsert(self) :
        self.storer = store.Store(self.store_path, self.map)

        time_start = time.time()
        for k in range(self.key_range) :
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            self.storer.insert(key, value)

        time_last = time.time() - time_start
        tps = self.key_range / time_last
        print 'test INSERT records=%d last %.2f seconds, tps=%.3f' % (self.key_range, time_last, tps)
        # testInsert()

    def     testReadWithoutCache(self) :
        self.storer = store.Store(self.store_path, self.map)

        times = 100000
        key_max = self.key_range - 1
        time_start = time.time()
        for i in range(times) :
            k = random.randint(0, key_max)
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            v = self.storer.find( key )

        count, hit, miss = self.storer.statCache()
        print '  - cache status: count=%d hit=%d miss=%d' % (count, hit, miss)

        time_last = time.time() - time_start
        tps = times / time_last
        print 'test READ-without-CACHE times=%d last %.2f seconds, tps=%.3f' % (times, time_last, tps)
        # testReadWithouCache()

    def     testReadWithCache(self, cache_size=1048576) :
        self.storer = store.Store(self.store_path, self.map, read_cache_size=cache_size)

        times = 300000
        key_max = self.key_range - 1
        time_start = time.time()
        for i in range(times) :
            k = random.randint(0, key_max)
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            v = self.storer.find( key )

        count, hit, miss = self.storer.statCache()
        print '  - cache status: count=%d hit=%d miss=%d' % (count, hit, miss)

        time_last = time.time() - time_start
        tps = times / time_last
        print 'test READ-without-CACHE cache-size:%d times=%d last %.2f seconds, tps=%.3f' % (cache_size, times, time_last, tps)
        # testReadWithCache()

    def     testInsertWithoutCache( self, record_count ) :
        self.storer = store.Store(self.store_path, self.map)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            self.storer.insert(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test INSERT without CACHE records=%d last %.2f seconds, tps=%.3f' % (record_count, time_last, tps)

        # verify insert ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - INSERT verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testInsertWithoutCache()

    def     testInsertWithCache( self, record_count, cache_size ) :
        self.storer = store.Store(self.store_path, self.map, write_cache_size = cache_size)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            self.storer.insert(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test INSERT with CACHE (cache-size:%d) records=%d last %.2f seconds, tps=%.3f' % (cache_size, record_count, time_last, tps)

        # verify insert ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value%090d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - INSERT verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testInsertWithCache()

    def     testUpdateWithoutCache( self, record_count ) :
        self.storer = store.Store(self.store_path, self.map)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-updated-%090d' % (k, )
            self.storer.update(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test UPDATE without CACHE records=%d last %.2f seconds, tps=%.3f' % (record_count, time_last, tps)

        # verify update ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-updated-%090d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - UPDATE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testUpdateWithoutCache()

    def     testUpdateWithCache( self, record_count, cache_size ) :
        self.storer = store.Store(self.store_path, self.map, write_cache_size = cache_size)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-updated-%090d' % (k, )
            self.storer.update(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test UPDATE with CACHE (cache-size:%d) records=%d last %.2f seconds, tps=%.3f' % (cache_size, record_count, time_last, tps)

        # verify update ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-updated-%090d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - UPDATE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testUpdateWithCache()

    def     testReplaceWithoutCache( self, record_count ) :
        self.storer = store.Store(self.store_path, self.map)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-replaced-%090d' % (k, )
            self.storer.replace(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test REPLACE without CACHE records=%d last %.2f seconds, tps=%.3f' % (record_count, time_last, tps)

        # verify replace ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-replaced-%090d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - REPLACE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testReplaceWithoutCache()

    def     testReplaceWithCache( self, record_count, cache_size ) :
        self.storer = store.Store(self.store_path, self.map, write_cache_size = cache_size)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-replaced-%09d' % (k, )
            self.storer.replace(key, value)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test REPLACE with CACHE (cache-size:%d) records=%d last %.2f seconds, tps=%.3f' % (cache_size, record_count, time_last, tps)

        # verify replace ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            value = 'value-replaced-%09d' % (k, )
            v = self.storer.find( key )
            if v == value :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
                print '"%s" vs "%s"' % (value, v)
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - REPLACE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testReplaceWithoutCache()

    def     testEraseWithoutCache( self, record_count ) :
        self.storer = store.Store(self.store_path, self.map)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            self.storer.erase(key)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test ERASE without CACHE records=%d last %.2f seconds, tps=%.3f' % (record_count, time_last, tps)

        # verify erase ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            v = self.storer.find( key )
            if v is None :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - ERASE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testEraseWithoutCache()

    def     testEraseWithCache( self, record_count, cache_size ) :
        self.storer = store.Store(self.store_path, self.map, write_cache_size = cache_size)
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            self.storer.erase(key)

        time_last = time.time() - time_start
        tps = record_count / time_last
        print 'test ERASE with CACHE (cache-size:%d) records=%d last %.2f seconds, tps=%.3f' % (cache_size, record_count, time_last, tps)

        # verify erase ok
        count_verify_ok = 0
        count_verify_bad = 0
        time_start = time.time()
        for k in range(record_count) :
            key = 'key%05d' % (k, )
            v = self.storer.find( key )
            if v is None :
                count_verify_ok += 1
            else :
                count_verify_bad += 1
        time_last = time.time() - time_start
        count_verify = count_verify_ok + count_verify_bad
        print '   - ERASE verify (total=%d ok=%d bad=%d) last %.2f seconds' % (count_verify, count_verify_ok, count_verify_bad, time_last)
        # testEraseWithoutCache()

    # class TestStore

def testErase() :
    record_count = 100000
    t = TestStore()
    t.setUp()
    t.testInsertWithoutCache(record_count)
    t.testEraseWithoutCache(record_count)
    t.tearDown()

    t.setUp()
    cache_size = 100
    t.testInsertWithCache(record_count, cache_size)
    t.testEraseWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 1000
    t.testInsertWithCache(record_count, cache_size)
    t.testEraseWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 10000
    t.testInsertWithCache(record_count, cache_size)
    t.testEraseWithCache(record_count, cache_size)
    t.tearDown()
    # testInsert()

def testReplace() :
    record_count = 100000
    t = TestStore()
    t.setUp()
    t.testInsertWithoutCache(record_count)
    t.testReplaceWithoutCache(record_count)
    t.tearDown()

    t.setUp()
    cache_size = 100
    t.testInsertWithCache(record_count, cache_size)
    t.testReplaceWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 1000
    t.testInsertWithCache(record_count, cache_size)
    t.testReplaceWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 10000
    t.testInsertWithCache(record_count, cache_size)
    t.testReplaceWithCache(record_count, cache_size)
    t.tearDown()
    # testInsert()

def testUpdate() :
    record_count = 100000
    t = TestStore()
    t.setUp()
    t.testInsertWithoutCache(record_count)
    t.testUpdateWithoutCache(record_count)
    t.tearDown()

    t.setUp()
    cache_size = 100
    t.testInsertWithCache(record_count, cache_size)
    t.testUpdateWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 1000
    t.testInsertWithCache(record_count, cache_size)
    t.testUpdateWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 10000
    t.testInsertWithCache(record_count, cache_size)
    t.testUpdateWithCache(record_count, cache_size)
    t.tearDown()
    # testInsert()

def testInsert() :
    record_count = 100000
    t = TestStore()
    t.setUp()
    t.testInsertWithoutCache(record_count)
    t.tearDown()

    t.setUp()
    cache_size = 100
    t.testInsertWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 1000
    t.testInsertWithCache(record_count, cache_size)
    t.tearDown()

    t.setUp()
    cache_size = 10000
    t.testInsertWithCache(record_count, cache_size)
    t.tearDown()
    # testInsert()

def testRead()  :
    t = TestStore()
    t.setUp()
    t.testInsert()
    t.testReadWithoutCache()
    #t.testReadWithCache(1024)
    #t.testReadWithCache(8192)
    #t.testReadWithCache(32768)
    #t.testReadWithCache(65536)
    t.testReadWithCache(1048576)
    t.tearDown()
    # testRead()

def main() :
    #testRead()
    #testInsert()
    #testUpdate()
    #testReplace()
    testErase()
    # main()


if    __name__ == '__main__' :
    main()
